---
title: 逐字符讲解 C 语言 HelloWorld 程序
description: 本文将逐字符讲解 C 语言 HelloWorld 程序，并详细介绍 C 语言的基本语法和常用预处理命令。
date: "2023-12-20"
lang: zh
tags:
  - C/C++
  - 编程
category: 技术
cover: https://cdn.qladgk.com/images/20250508161225090.png
---

import Image from "@/components/mdx/Image";

<Image
  src="https://cdn.qladgk.com/images/20250508161225090.png"
  alt="cover"
  width={999}
  height={527}
  isArticleImage={true}
/>

C 语言往往是我们接触到的第一门语言，而 HelloWorld 往往又是我们第一次运行的代码，但是你真的了解 C 语言的 HelloWorld 吗？

```c
#include <stdio.h>
// 我的第一个 C 语言程序
int main(int argc, char *argv[])
{
		printf("Hello, World! \n");
		return 0;
}
```

> 有的可能不写 `return 0`
>
> 有的可能会将返回参数从 `int` 写成 `void`
>
> 有的可能不写参数 `~~int argc, char *argv[]~~`

以上是一个最标准的 C 语言 HelloWorld 代码，写法上的差异放在最后讲解

## 一、# 预处理

`#` 为 **预处理标示符**，顾名思义是在编译之前执行的事情

以下为常见的预处理命令

| 指令       | 描述                                                        |
| ---------- | ----------------------------------------------------------- |
| `#define`  | 定义宏                                                      |
| `#include` | 包含一个源代码文件                                          |
| `#undef`   | 取消已定义的宏                                              |
| `#ifdef`   | 如果宏已经定义，则返回真                                    |
| `#ifndef`  | 如果宏没有定义，则返回真                                    |
| `#if`      | 如果给定条件为真，则编译下面代码                            |
| `#else`    | #if 的替代方案                                              |
| `#elif`    | 如果前面的 #if 给定条件不为真，当前条件为真，则编译下面代码 |
| `#endif`   | 结束一个 #if……#else 条件编译块                              |
| `#error`   | 当遇到标准错误时，输出错误消息                              |
| `#pragma`  | 使用标准化方法，向编译器发布特殊的命令到编译器中            |

### 1、#include

include 包含，`#include` 是一个预处理命令，用来 **引入头文件**，效果是直接把头文件的内容复制在代码上方，可减少代码量

### 2、.h 头文件

以 .h 为拓展名的是 C 语言的头文件，包含了 C **函数声明和宏定义**，被多个源文件中引用共享。

**_有两种类型的头文件：程序员编写的头文件和编译器自带的头文件。_**

要在程序中使用头文件，需要使用 C 预处理指令  `#include`  来引用它。

引用头文件的语法有以下两种

```c
#include <file>
```

```c
#include "file"
```

### 3、stdio.h 文件

- std：代表符合 **standard 标准**
- i：代表 input 输入
- o：代表 output 输出

因此，`stdio.h` 是 C 语言标准库中的一个头文件，提供了标准输入输出的函数声明。

这个头文件是由 C 语言标准规定的，因此是编译器自带的标准头文件之一。

**_编译器自带的头文件放在 `/usr/include/` 目录下_**

![](https://s2.loli.net/2023/12/20/rPX8jIMilOE9HCa.png)

可以看到 `printf` 函数被定义在这个文件中

**_只有包含了 `stdio.h` 头文件才能使用 `printf` 函数_**

---

## 二、main() 函数

在 C 语言中，`main` 函数是程序的入口点，也是程序执行的起始位置。每个 C 程序都必须包含一个 main 函数。当程序运行时，操作系统调用 main 函数来启动程序的执行。

`main` 函数的基本形式如下：

```c
int main(void)
{
// 代码块
    return 0; // 可选的返回值，表示程序成功结束
}
```

这里有一些关键点：

1. **函数签名：** `main` 函数的返回类型通常是整数类型，一般是 `int`。返回值可以用来指示程序的执行状态，一般约定 **返回 0** 表示程序成功结束，而其他值则表示错误或异常情况。
2. **参数：** `main` 函数可以接受两个参数，用于处理命令行参数。

   ```c
   int main(int argc, char *argv[])
   {
       // 代码块
       return 0;
   }
   ```

   或者可以使用不带参数的形式：

   ```c
   int main(void){
       // 代码块
       return 0;
   }
   ```

   **_注意，如果直接使用 `int main()` 在标准 C 中也是合法的，但有些编译器可能对参数的形式有一些要求。_**

   因此，如果你的编译器对带参数的 main 有特殊要求，可以选择使用 `int main(void)` 形式来明确表示不接受参数。

3. **代码块：** `main` 函数的主体包含了程序的实际执行代码。这是程序的主要逻辑所在。
   ```c
   printf("Hello World!);
   ```
4. **返回值：** `return 0;` 是可选的，但通常在 `main` 函数中使用，0 是程序正常的状态码，表示程序成功结束。其他非零的返回值通常用于指示程序异常结束或错误状态。

**_整个程序的执行流程是从 main 函数开始，然后按照代码中的逻辑依次执行，直至 main 函数的末尾。_**

---

## 三、本例中包含的 C 令牌（Token）

C 程序由各种令牌组成，令牌可以是关键字、标识符、常量、字符串值，或者是一个符号。例如，下面的 C 语句包括五个令牌：

```c
printf("Hello, World! \n");
```

1. **`printf`**：这是一个标识符（Identifier），表示调用的函数名。
2. **`(`**：这是一个左圆括号，表示函数调用的开始。
3. **`"Hello, World! \n"`**：这是一个字符串常量，表示要被打印输出的字符串。
4. **`)`**：这是一个右圆括号，表示函数调用的结束。
5. **`;`**：这是一个分号，表示语句的结束。

> 每个令牌在 C 语言中都有特定的含义和用途。这些令牌共同构成了一条 C 语句的结构。在上述例子中，printf 是一个函数调用语句，包含了一个字符串常量作为参数，然后以分号结束这条语句。

### 1、标识符

C 标识符 **是用来标识** 变量、函数，或任何其他用户自定义 **项目的名称**。一个标识符以字母 A-Z 或 a-z 或下划线 \_ 开始，后跟零个或多个字母、下划线和数字（0-9）。

C 标识符内不允许出现标点字符，比如 @、$ 和 %。C 是 **区分大小写** 的编程语言。因此，在 C 中，*Manpower*  和  *manpower*  是两个不同的标识符。下面列出几个有效的标识符：

```c
mohd zara abc move_name a_123myname50 _temp j a23b9 retVal
```

### 2、分号

在 C 程序中，分号是 **语句结束符**。也就是说，每个语句必须以分号结束。它表明一个逻辑实体的结束。

例如，下面是两个不同的语句：

```c
printf("Hello, World! \n");
return 0;
```

**_在没有分号的情况下，编译器将无法正确解析代码结构，因此分号是 C 语言中必不可少的语法元素。_**

### 3、注释

注释是用来提高代码可读性和维护性的工具，可以用于添加说明、临时禁用代码等。在编译时，注释的内容会被完全忽略。

在 C 语言中，有两种主要的注释形式，分别是单行注释和多行注释。

- 单行注释

  使用 `//` 进行单行注释。在 `//` 出现的位置，其后的内容都被视为注释，直到该行结束。

  ```c
  // 这是单行注释，这一行的内容都是注释
  int main()
  {
      // 这是 main 函数
      printf("Hello, world!\n"); 	// 这是打印语句
      return 0; // 返回 0 表示程序正常结束
  }
  ```

- 多行注释

  使用 `/*` 开始注释块，使用 `*/` 结束注释块。注释块中的所有内容都被视为注释。

  ```c
  /*这是多行注释*/
  int main()
  {
      /*
      这是 main 函数
      这是另一个注释块
      */
      printf("Hello, world!\n");	/* 这是打印语句 */
      return 0; /* 返回 0 表示程序正常结束 */
  }
  ```

**_多行注释不能嵌套使用，例如 `/_ /_ 这是注释 _/ _/` 是不合法的。_**

---

## 四、转义字符

在 C 语言中，转义字符是一种特殊的字符序列，用于表示一些无法直接输入或打印的字符。转义字符以反斜杠（`\`）开头，后跟一个或多个字符。

转义字符的初衷是用于 ASCII 编码，所以它的取值范围有限：

- 八进制形式的转义字符最多后跟三个数字，也即`\ddd`，最大取值是`\177`；
- 十六进制形式的转义字符最多后跟两个数字，也即`\xdd`，最大取值是`\x7f`。

超出范围的转义字符的行为是未定义的，有的编译器会将编码值直接输出，有的编译器会报错。

不过，直接使用 ASCII 码记忆不方便，也不容易理解，所以，针对常用的控制字符，C 语言又定义了简写方式，完整的列表如下：

| 转义字符 | 意义                                | ASCII 码值（十进制） |
| -------- | ----------------------------------- | -------------------- |
| \a       | 响铃(BEL)                           | 007                  |
| \b       | 退格(BS) ，将当前位置移到前一列     | 008                  |
| \f       | 换页(FF)，将当前位置移到下页开头    | 012                  |
| \n       | 换行(LF) ，将当前位置移到下一行开头 | 010                  |
| \r       | 回车(CR) ，将当前位置移到本行开头   | 013                  |
| \t       | 水平制表(HT)                        | 009                  |
| \v       | 垂直制表(VT)                        | 011                  |
| \'       | 单引号                              | 039                  |
| \"       | 双引号                              | 034                  |
| \\       | 反斜杠                              | 092                  |

例如，本例中提到的 `"Hello, World! \n"` 中的 `\n` 表示换行符，使得在输出字符串后会在控制台中换行显示。

![](https://s2.loli.net/2023/12/20/TZCGjiJf7uSNEM5.png)

---

## 五、总结

1. **包含头文件：** `#include <stdio.h>` 表示引入标准输入输出库的头文件，其中包含了 `printf` 函数的声明。
2. **注释：** `// 我的第一个 C 语言程序` 是一个单行注释，用于提供对程序的简要描述。
3. **main 函数：** `int main(int argc, char *argv[])` 是程序的入口点。`argc` 表示命令行参数的数量，`argv` 是一个指向字符串数组的指针，存储了实际的命令行参数。
4. **printf 函数：** `printf("Hello, World! \n");` 是一个输出语句，它将字符串 “Hello, World!” 和一个换行符打印到标准输出设备（通常是控制台）。
5. **返回值：** `return 0;` 表示 `main` 函数成功执行并正常结束，返回值为 0。在 C 语言中，返回值为 0 通常表示程序成功执行。

这个程序的主要目的是输出 “Hello, World!” 到控制台，这是一个传统的、用于学习编程语言的入门示例。

---

## 六、争论写法上的差异问题

- `int main` 还是 `void main` ?

  标准规定 `main` 函数返回一个整数值，通常用于指示程序的执行状态。返回值为 0 通常表示程序正常结束，而其他非零值则表示程序异常结束或错误状态。

  因此，正确的形式是：

  ```c
  int main()
  {
      // 你的代码
      return 0;
  }
  ```

  > 不推荐使用 `void main()`，因为根据 C 语言标准，`main` 函数应该返回一个整数值。虽然有些编译器可能会接受 `void main()`，但这并不是标准的写法，可能导致不同编译器之间的兼容性问题。

- 该不该写 `main` 函数的那两个参数？

  在 C 语言中，`main` 函数的标准形式是带有参数，即 `int main(int argc, char *argv[])`

  然而，如果你选择不写这两个参数，编译器通常也会接受。

  ```c
  // 如本例中完全可以不写两个参数
  #include <stdio.h>
  int main()
  {
      printf("Hello, World!\n");
      return 0;
  }
  ```

  这个程序是合法的，并且在很多编译器中会正常工作。

  在这个特定的例子中，省略了 `argc` 和 `argv` 参数，因为这个简单的程序没有使用命令行参数。

  > 在实际应用中，是否使用带参数的 `main` 函数取决于你的程序是否需要处理命令行输入。如果不需要，可以使用不带参数的形式。

- 不写 `return 0` 会怎么样?

  按照约定，返回值为 0 表示程序正常结束，而其他非零值通常表示程序发生了错误或异常情况。如果省略 `return` 语句，编译器通常会 **默认返回 0**，但一些编译器可能会给出警告，因为这样的代码可能会让人误解程序的执行状态。

  ![](https://s2.loli.net/2023/12/20/xhR7mgo4CIzAJPW.png)

  > 在这个例子中，虽然缺少了 `return 0;`，但许多编译器仍然会默认认为程序正常结束，返回值为 0。然而，为了养成良好的编程习惯，强烈建议在 `main` 函数中使用 `return` 语句，以提高代码的清晰性和可读性。

---

> 原视频如下

<iframe
  src="//player.bilibili.com/player.html?isOutside=true&aid=434407138&bvid=BV1T3411D773&cid=941679314&p=1"
  scrolling="no"
  border="0"
  frameborder="no"
  framespacing="0"
  allowfullscreen="true"
></iframe>
